﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
using static HackWinMine.WinMineData;

namespace HackWinMine
{
    //主要实现两部分操作：获取、修改程序数据，模拟点击界面
    internal class WinMineCrack
    {
        #region 引用DLL
        [DllImport("kernel32.dll")]
        public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);

        [DllImport("kernel32.dll")]
        public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, out int lpNumberOfBytesRead);
        [DllImport("kernel32.dll")]
        public static extern bool CloseHandle(IntPtr hObject);
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
        [DllImport("user32.dll", SetLastError = true)]
        static extern bool SetForegroundWindow(IntPtr hWnd);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
        #endregion
        #region 进程定义
        const uint WMN_DATA_OFFSET = 0x01005340;
        #endregion
        #region 窗口参数定义
        const int WMN_CELL_WIDTH = 24;
        const int WMN_CELL0_XOFFSET = 20;
        const int WMN_CELL0_YOFFSET = 85;

        // 定义鼠标事件消息
        const uint WM_LBUTTONDOWN = 0x0201; // 鼠标左键按下
        const uint WM_LBUTTONUP = 0x0202; // 鼠标左键抬起
        const uint WM_RBUTTONDOWN = 0x0204; // 鼠标左键按下
        const uint WM_RBUTTONUP = 0x0205; // 鼠标左键抬起
        const uint WM_CLICK = 0xF5; // 点击消息

        const int PROCESS_VM_READ = 0x0010;
        const int PROCESS_VM_WRITE = 0x0020;
        #endregion
        #region 局部变量
        IntPtr processHwnd = IntPtr.Zero;
        WinMineData winMineData = new WinMineData();
        #endregion

        public int RefreshData()
        {
            byte[] wmnDataBuf = new byte[WinMineData.WMN_DATA_BUFLEN];
            int processId = 0; /* winmine.exe的进程ID */
            Process[] processes = Process.GetProcessesByName("winmine");
            if (processes.Length == 0)
            {
                return -1;
            }
            
            processId = processes[0].Id;
            IntPtr processHandle = OpenProcess(PROCESS_VM_READ, false, processId);

            if (processHandle != IntPtr.Zero)
            {
                IntPtr baseAddress = (IntPtr)WMN_DATA_OFFSET;
                int bytesRead;
                if (ReadProcessMemory(processHandle, baseAddress, winMineData.WmnDataBuf, winMineData.WmnDataBuf.Length, out bytesRead))
                {
                    ;
                }
                // 关闭进程句柄
                CloseHandle(processHandle);
            }
            return 0;
        }

        public int Crack()
        {
            int ret;
            
            processHwnd = FindWindow(null, "扫雷");
            if (processHwnd == IntPtr.Zero)
                return -1;
            ret = RefreshData();
            if (ret == -1)
                return ret;
            int rows = winMineData.Rows;
            int cols = winMineData.Columns;
            for (int i = 0; i < rows; i++)
            {
                for (int j = 0; j < cols; j++)
                {
                    WinMineData.ENUM_MINE_DATA mineCellData = winMineData.getCellData(i, j);
                    switch(mineCellData)
                    {
                        case ENUM_MINE_DATA.EMPTY:
                            SendMouseLbtnClickToWindow(processHwnd, j, i);
                            break;
                        case ENUM_MINE_DATA.MINE:
                            SendMouseRbtnClickToWindow(processHwnd,j,i);
                            break;
                    }
                }
            }
            return 0;
        }

        //随机点开一个空格
        public int CrackNext()
        {
            int ret;
            int retry = 0;
            processHwnd = FindWindow(null, "扫雷");
            if (processHwnd == IntPtr.Zero)
                return -1;
            ret = RefreshData();
            if (ret == -1)
                return ret;
            int rows = winMineData.Rows;
            int cols = winMineData.Columns;
            Random random = new Random();
            for(int i = 0; i < 100; i++)
            {
                int row = random.Next(rows);
                int col = random.Next(cols);
                WinMineData.ENUM_MINE_DATA mineCellData = winMineData.getCellData(row, col);
                if(mineCellData== ENUM_MINE_DATA.EMPTY)
                {
                    SendMouseLbtnClickToWindow(processHwnd, col, row);
                    if (retry++ == 10)
                        break;
                }
            }
            return 0;
        }
        private void SendMouseRbtnClickToWindow(IntPtr windowHandle, int x, int y)
        {
            SetForegroundWindow(windowHandle);
            // 计算lParam参数，包含鼠标位置x和y，高16bit存储y值，低16bit存储x值
            int lParam = (85 + 24 * y << 16) + 20 + 24 * x;

            // 发送鼠标左键按下和抬起事件
            SendMessage(windowHandle, WM_RBUTTONDOWN, IntPtr.Zero, (IntPtr)lParam);
            SendMessage(windowHandle, WM_RBUTTONUP, IntPtr.Zero, (IntPtr)lParam);
        }

        private void SendMouseLbtnClickToWindow(IntPtr windowHandle, int x, int y)
        {
            SetForegroundWindow(windowHandle);
            // 计算lParam参数，包含鼠标位置x和y，高16bit存储y值，低16bit存储x值
            int lParam = (85 + 24 * y << 16) + 20 + 24 * x;

            // 发送鼠标左键按下和抬起事件
            SendMessage(windowHandle, WM_LBUTTONDOWN, IntPtr.Zero, (IntPtr)lParam);
            SendMessage(windowHandle, WM_LBUTTONUP, IntPtr.Zero, (IntPtr)lParam);
        }

        //两个按键同时按下
        private void SendMouseLRbtnClickToWindow(IntPtr windowHandle, int x, int y)
        {
            SetForegroundWindow(windowHandle);
            // 计算lParam参数，包含鼠标位置x和y，高16bit存储y值，低16bit存储x值
            int lParam = (85 + 24 * y << 16) + 20 + 24 * x;

            // 发送鼠标左键按下和抬起事件
            SendMessage(windowHandle, WM_LBUTTONDOWN, IntPtr.Zero, (IntPtr)lParam);
            SendMessage(windowHandle, WM_RBUTTONDOWN, IntPtr.Zero, (IntPtr)lParam);
            SendMessage(windowHandle, WM_LBUTTONUP, IntPtr.Zero, (IntPtr)lParam);
            SendMessage(windowHandle, WM_RBUTTONUP, IntPtr.Zero, (IntPtr)lParam);
        }

        public ENUM_MINE_DATA getFrameData(int row, int col)
        {
            return winMineData.getFrameData(row, col);
        }
    }
}
